package vn.edu.hut.soict.translate.transform;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.swing.GroupLayout.Alignment;

import vn.edu.hut.soict.lienkate.parser.Connection;
import vn.edu.hut.soict.translate.transform.utils.GrammaticalConstant;
import vn.edu.hut.soict.translate.transform.utils.GrammaticalNumber;
import vn.edu.hut.soict.translate.transform.utils.VerbTransformUtils;

public class InsertionTransformer extends Transformer {
	private List<Integer> inserted;
	private Map<Integer, Integer> insertMap;

	public InsertionTransformer(InnerTransformer transformer) {
		super(transformer);
		deletedConnections = new ArrayList<Connection>();
		inserted = new ArrayList<Integer>();
		for (int i = 0; i < inputTokenList.size(); i++) {
			if (inputToSourceMap.get(i) != null) {
				inserted.add(i);
			}
		}
		insertMap = new HashMap<Integer, Integer>();
	}

	/*
	 * Insert a token from the input token list to the destination token list.
	 * @param i the position of the token
	 * @param newWord the word (in destination language)
	 */
	public void insert(int i, String word) {
		if (inputLinkage == null || destinationLinkage == null) {
			insertToDestination(i, word);
			return;
		}

		for (Connection connection : inputLinkage.getLink().get(i)) {
			if (leftToRightInsert(i, word, connection)) {
				return;
			}
		}

		for (int j = 0; j < inputLinkage.getLink().size(); j++) {
			LinkedList<Connection> links = inputLinkage.getLink().get(j);
			for (Connection connection : links) {
				if (i == connection.right() && rightToLeftInsert(i, word, connection)) {
					return;
				}
			}
		}

		insertToDestination(i, word);
	}

	private boolean leftToRightInsert(int i, String word, Connection connection) {
		if (insertTenseOfVerb(i, connection)) {
			return true;
		}
		return false;
	}

	private boolean rightToLeftInsert(int j, String word, Connection connection) {
		if (insertAdjective(j, word, connection)) {
			return true;
		}
		return false;
	}

	private boolean insertAdjective(int j, String word, Connection connection) {
		String type = connection.type();
		int i = connection.right();

		if (type.startsWith("DT_TT") || type.startsWith("SDT6")) {
			insertReversedToDestination(i, word);
			return true;
		}

		return false;
	}

	private boolean insertTenseOfVerb(int i, Connection connection) {
		String type = connection.type();
		int j = connection.right();
		List<Integer> indexes = alignmentParser.destinationIndexesAlignWith(j);

		if (indexes == null) {
			return false;
		}
		else if (indexes.size() > 1) {
			System.err.println("Not supported yet");
		}

		if (type.startsWith("TĐT3") && type.length() >= 5) {
			int verbIndex = indexes.get(0);
			int subjectIndex = -1;
			String verb = destinationTokenList.get(verbIndex);

			subjectIndex = findRightConnection(destinationLinkage, verbIndex, "S", null);
			if (subjectIndex == -1) {
				int temp = findRightConnection(destinationLinkage, verbIndex, "Pg", null);
				subjectIndex = findRightConnection(destinationLinkage, temp, "S", null);
			}
			if (subjectIndex == -1) {
				int temp = findRightConnection(destinationLinkage, verbIndex, "PP", null);
				subjectIndex = findRightConnection(destinationLinkage, temp, "S", null);
			}
			if (subjectIndex == -1) {
				int temp = findRightConnection(destinationLinkage, verbIndex, "I", null);
				subjectIndex = findRightConnection(destinationLinkage, temp, "S", null);
			}
			if (subjectIndex == -1) {
				int temp = findRightConnection(destinationLinkage, verbIndex, "I", null);
				temp = findRightConnection(destinationLinkage, temp, "TO", null);
				temp = findRightConnection(destinationLinkage, temp, "Pg", null);
				subjectIndex = findRightConnection(destinationLinkage, temp, "S", null);
			}
			if (subjectIndex == -1) {
				System.err.println("Cannot find subject of verb.");
				return false;
			}

			String subject = destinationTokenList.get(subjectIndex);
			GrammaticalConstant numberSubject = GrammaticalNumber.nounOrPronounNumber(subject);

			char tense = type.charAt(4);
			switch (tense) {
				case 'q':
					verb = VerbTransformUtils.conjugate(verb, GrammaticalConstant.PAST_SIMPLE);
					destinationTokenList.set(verbIndex, verb);
					break;
				case 'c':
					verb = VerbTransformUtils.conjugate(verb, GrammaticalConstant.PRESENT_PARTICIPLE);
					destinationTokenList.set(verbIndex, verb);
					if (subject.equalsIgnoreCase("I")) {
						insertBefore(i, verbIndex, "am");
					}
					else if (numberSubject == GrammaticalConstant.SINGULAR) {
						insertBefore(i, verbIndex, "is");
					}
					else {
						insertBefore(i, verbIndex, "are");
					}
					break;
				case 't':
					verb = VerbTransformUtils.conjugate(verb, GrammaticalConstant.BARE);
					destinationTokenList.set(verbIndex, verb);
					insertBefore(i, verbIndex, "will");
					break;
				case 'h':
					verb = VerbTransformUtils.conjugate(verb, GrammaticalConstant.PAST_PARTICIPLE);
					destinationTokenList.set(verbIndex, verb);
					if (!subject.equalsIgnoreCase("I") && numberSubject == GrammaticalConstant.SINGULAR) {
						insertBefore(i, verbIndex, "has");
					}
					else {
						insertBefore(i, verbIndex, "have");
					}
					break;
				case 'g':
					verb = VerbTransformUtils.conjugate(verb, GrammaticalConstant.BARE);
					destinationTokenList.set(verbIndex, verb);
					if (subject.equalsIgnoreCase("I")) {
						insertBefore(i, verbIndex, "am going to");
					}
					else if (numberSubject == GrammaticalConstant.SINGULAR) {
						insertBefore(i, verbIndex, "is going to");
					}
					else {
						insertBefore(i, verbIndex, "are going to");
					}
					break;
			}

			return true;
		}

		if (type.startsWith("TTT3") && type.length() >= 5) {
			int adjectiveIndex = indexes.get(0);
			int subjectIndex = -1;
			String verb = destinationTokenList.get(adjectiveIndex);

			int beIndex = findRightConnection(destinationLinkage, adjectiveIndex, "Pa", null);
			subjectIndex = findRightConnection(destinationLinkage, beIndex, "S", null);
			if (subjectIndex == -1) {
				int temp = findRightConnection(destinationLinkage, beIndex, "PP", null);
				subjectIndex = findRightConnection(destinationLinkage, temp, "S", null);
			}
			if (subjectIndex == -1) {
				int temp = findRightConnection(destinationLinkage, beIndex, "I", null);
				subjectIndex = findRightConnection(destinationLinkage, temp, "S", null);
			}
			if (subjectIndex == -1) {
				System.err.println("Cannot find subject of adjective.");
				return false;
			}

			String subject = destinationTokenList.get(subjectIndex);
			GrammaticalConstant numberSubject = GrammaticalNumber.nounOrPronounNumber(subject);

			char tense = type.charAt(4);
			switch (tense) {
				case 'q':
					if (numberSubject == GrammaticalConstant.SINGULAR) {
						destinationTokenList.set(beIndex, "was");
					}
					else {
						destinationTokenList.set(beIndex, "were");
					}
					break;
				case 't':
				case 'g':
					destinationTokenList.set(beIndex, "be");
					insertBefore(i, beIndex, "will");
					break;
				case 'h':
					destinationTokenList.set(beIndex, "been");
					if (!subject.equalsIgnoreCase("I") && numberSubject == GrammaticalConstant.SINGULAR) {
						insertBefore(i, beIndex, "has");
					}
					else {
						insertBefore(i, beIndex, "have");
					}
					break;
			}

			return true;
		}

		return false;
	}

	private void insertToDestination(int i, String word) {
		int left = -1;
		int right = -1;
		if (inputLinkage != null) {
			left = leftRelatedIndex(i);
			right = rightRelatedIndex(i);
		}
		int j = -1;
		if (left != -1) {
			j = insertedIndex(left);
			insertAfter(i, j, word);
		}
		else if (right != -1) {
			j = insertedIndex(right);
			insertBefore(i, j, word);
		}
		else {
			left = leftIndex(i);
			insertAfter(i, j, word);
		}
		inserted.add(i);
	}

	private void insertReversedToDestination(int i, String word) {
		int left = -1;
		int right = -1;
		if (inputLinkage != null) {
			left = leftRelatedIndex(i);
			right = rightRelatedIndex(i);
		}
		int j = -1;
		if (left != -1) {
			j = insertedIndex(left);
			insertBefore(i, j, word);
		}
		else if (right != -1) {
			j = insertedIndex(right);
			insertAfter(i, j, word);
		}
		else {
			left = leftIndex(i);
			insertBefore(i, j, word);
		}
		inserted.add(i);
	}

	private int insertedIndex(int i) {
		Integer sourceIndex = inputToSourceMap.get(i);
		if (sourceIndex != null) {
			List<Integer> list = alignmentParser.destinationIndexesAlignWith(sourceIndex);
			if (list != null) {
				return list.get(list.size() - 1);
			}
			else {
				return -1;
			}
		}
		else {
			return insertMap.get(i);
		}
	}

	private void insertBefore(int i, int j, String word) {
		destinationTokenList.add(j, word);
		updateAlignment(i, j);
	}

	private void insertAfter(int i, int j, String word) {
		if (i < destinationTokenList.size() - 1) {
			destinationTokenList.add(j + 1, word);
			updateAlignment(i, j + 1);
		}
		else {
			destinationTokenList.add(word);
			updateAlignment(i, j + 1);
		}
	}

	private void updateAlignment(int i, int j) {
		for (List<Integer> value : alignmentParser.getAlignment().values()) {
			for (int k = 0; k < value.size(); k++) {
				if (value.get(k) != null && value.get(k) >= j) {
					value.set(k, value.get(k) + 1);
				}
			}
		}

		for (int k = destinationTokenList.size() - 1; k >= j; k--) {
			List<Integer> list = alignmentParser.getReversedAlignment().get(k - 1);
			alignmentParser.getReversedAlignment().remove(k - 1);
			alignmentParser.getReversedAlignment().put(k, list);
		}

		insertMap.put(i, j);
	}

	private int leftIndex(int i) {
		for (int j = i - 1; j >= 0; j--) {
			if (inserted.contains(j)) {
				return j;
			}
		}
		return -1;
	}

	private int rightIndex(int i) {
		for (int j = i + 1; j < inputTokenList.size(); j++) {
			if (inserted.contains(j)) {
				return j;
			}
		}
		return inputTokenList.size();
	}

	private int leftRelatedIndex(int i) {
		int leftIndex = -1;

		for (int left = i - 1; left >= 0; left--) {
			LinkedList<Connection> links = inputLinkage.getLink().get(left);
			for (Connection connection : links) {
				if (i == connection.right()) {
					leftIndex = rightMost(left, i);
					if (leftIndex == -1 && inserted.contains(left)) {
						leftIndex = left;
					}
					if (leftIndex >= 0) {
						return leftIndex;
					}
				}
			}
		}

		return -1;
	}

	private int rightRelatedIndex(int i) {
		int rightIndex = -1;

		for (int j = 0; j < inputLinkage.getLink().get(i).size(); j++) {
			int right = inputLinkage.getLink().get(i).get(j).right();
			rightIndex = leftMost(right, i);
			if (rightIndex == -1 && inserted.contains(right)) {
				rightIndex = right;
			}
			if (rightIndex >= 0) {
				return rightIndex;
			}
		}

		return -1;
	}

	private int leftMost(int i, int lower) {
		int leftMost = -1;
		for (int left = i - 1; left > lower; left--) {
			LinkedList<Connection> links = inputLinkage.getLink().get(left);
			for (Connection connection : links) {
				if (i == connection.right()) {
					leftMost = leftMost(left, lower);
					if (leftMost == -1) {
						leftMost = left;
					}
					if (leftMost >= 0 && inserted.contains(leftMost)) {
						return leftMost;
					}
				}
			}
		}

		return -1;
	}

	private int rightMost(int i, int upper) {
		int rightMost = -1;
		for (int j = inputLinkage.getLink().get(i).size() - 1; j >= 0; j--) {
			int right = inputLinkage.getLink().get(i).get(j).right();
			if (right >= upper) {
				continue;
			}
			rightMost = rightMost(right, upper);
			if (rightMost == -1) {
				rightMost = right;
			}
			if (rightMost >= 0 && inserted.contains(rightMost)) {
				return rightMost;
			}
		}

		return -1;
	}

}
